package com.lightning.modules.validator;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Service;

import com.lightning.modules.validator.data.Command;
import com.lightning.modules.validator.data.Field;
import com.lightning.modules.validator.data.Method;
import com.lightning.modules.validator.data.Valid;
import com.lightning.modules.validator.data.Validators;
import com.lightning.modules.validator.validators.AbstractValidator;

@Service
public class XmlResolver implements ValidatorResolver {
	private final String CMD_EL_ATR_PROPERTY = "property";
	private final String CMD_EL_ATR_PARMETER = "parmeter";
	private final String CMD_EL_ATR_NOTICE = "notice";
	private final String METHOD_EL = "method";
	private final String FIELDS_EL = "fields";
	private final String FIELD_EL = "field";
	private final String VALID_EL = "valid";
	private final String xmlDir;

	public XmlResolver() {
		xmlDir = this.getClass().getResource("/").getPath() + "../validator";
	}

	@SuppressWarnings("unchecked")
	public List<AbstractValidator> resolveValidator() {
		SAXReader reader = new SAXReader();
		Validators validators = new Validators();
		Map<String, String> clazzMap = new HashMap<String, String>();
		try {

			Document doc = reader.read(getFilePrefix(xmlDir) + "validator.xml");
			Element validatorsEL = doc.getRootElement();
			Iterator<Element> validatorIter = validatorsEL.elementIterator("validator");
			while (validatorIter.hasNext()) {
				Element validator = validatorIter.next();
				String name = validator.attributeValue("name");
				String clazz = validator.attributeValue("class");
				clazzMap.put(name, clazz);
				AbstractValidator validatorClazz = (AbstractValidator) Class.forName(clazz).newInstance();
				validatorClazz.setName(name);
				validators.getValidator().add(validatorClazz);
			}
			Iterator<Element> extValidatorIter = validatorsEL.elementIterator("extendValidator");
			while (extValidatorIter.hasNext()) {
				Element validator = extValidatorIter.next();
				String name = validator.attributeValue("name");
				String clazz = validator.attributeValue("validator");
				clazz = clazzMap.get(clazz);
				String parmeter = validator.attributeValue("parmeter");
				AbstractValidator validatorClazz = (AbstractValidator) Class.forName(clazz).newInstance();
				validatorClazz.setName(name);
				validatorClazz.setParmeter(parmeter);
				validators.getValidator().add(validatorClazz);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return validators.getValidator();
	}

	@SuppressWarnings("unchecked")
	public List<Command> resolverCommand() {
		List<Command> list = new ArrayList<Command>();
		SAXReader reader = new SAXReader();
		File[] files = null;
		files = listAllFile(getFilePrefix(xmlDir));
		for (File file : files) {
			try {
				Document doc = reader.read(file);
				Element commandEL = doc.getRootElement();
				Command command = new Command();
				command.setProperty(commandEL.attributeValue(CMD_EL_ATR_PROPERTY));
				Iterator<Element> fieldELIter = commandEL.elementIterator(FIELD_EL);
				while (fieldELIter.hasNext()) {
					Element fieldEL = fieldELIter.next();
					Field field = new Field(fieldEL.attributeValue(CMD_EL_ATR_PROPERTY));
					// valid
					Iterator<Element> validELIter = fieldEL.elementIterator(VALID_EL);
					while (validELIter.hasNext()) {
						Element validEL = validELIter.next();
						Valid valid = new Valid();
						valid.setProperty(validEL.attributeValue(CMD_EL_ATR_PROPERTY));
						valid.setParmeter(validEL.attributeValue(CMD_EL_ATR_PARMETER));
						valid.setNotice(validEL.attributeValue(CMD_EL_ATR_NOTICE));
						field.addValidator(valid);
					}
					command.getFields().put(field.getProperty(), field);
				}
				Iterator<Element> methodELIter = commandEL.elementIterator(METHOD_EL);
				while (methodELIter.hasNext()) {
					Element methodEL = methodELIter.next();
					Method method = new Method();
					method.setProperty(methodEL.attributeValue(CMD_EL_ATR_PROPERTY));
					// fields
					Element fieldsEL = methodEL.element(FIELDS_EL);
					String fields = fieldsEL.attributeValue(CMD_EL_ATR_PROPERTY);
					Set<String> fieldSet = new HashSet<String>();
					if (fields != null && !"".equals(fields.trim())) {
						String[] fieldArr = fields.replaceAll(" ", "").split(",");
						fieldSet.addAll(Arrays.asList(fieldArr));
					}
					fieldELIter = fieldsEL.elementIterator(FIELD_EL);
					while (fieldELIter.hasNext()) {
						Element fieldEL = fieldELIter.next();
						fieldSet.add(fieldEL.attributeValue(CMD_EL_ATR_PROPERTY));
					}
					if (fieldSet.size() > 0) {
						for (String string : fieldSet) {
							if (command.getFields().containsKey(string)) {
								method.getFields().getFields().add(command.getFields().get(string));
							}
						}
					}
					command.getMethods().put(method.getProperty(), method);
				}
				list.add(command);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return list;
	}

	private String getFilePrefix(String prefix) {
		return prefix + (prefix.endsWith("/") ? "" : "/");
	}

	private File[] listAllFile(String root) {
		File file = new File(root);
		File[] files = file.listFiles(new FileFilter() {
			public boolean accept(File pathname) {
				return pathname.getName().endsWith(".val.xml");
			}
		});
		return files;
	}
}
